#include "sys_hc32f460.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

const uint8_t SOI = 0x7E; // 开始字符是固定的0x7E => ~
const uint8_t EOI = 0x0D; // 结束字符是固定的0x0D

void calc_and_print_YND23_cmd(const char* psz_tip, uint8_t VER, uint8_t ADR, uint8_t CID1, uint8_t CID2, uint8_t* INFO, uint16_t LENGTH, uint8_t* YND23_cmd);

int32_t gen_YND23_cmd(uint8_t VER, uint8_t ADR, uint8_t CID1, uint8_t CID2, uint16_t LENGTH, uint8_t* INFO, uint8_t* YND23_cmd);
void uint8_to_2ascii_int8_t(uint8_t uc_in, uint8_t * c1, uint8_t * c2);
void uint16_to_4ascii_int8_t(uint32_t ui_in, uint8_t * c1, uint8_t * c2, uint8_t * c3, uint8_t * c4);
int8_t fn_4bits_to_int8_t(uint8_t uc);
uint16_t LENGTH_add_LCHKSUM(uint16_t LENGTH);
uint16_t calc_YDN23_CHKSUM(uint8_t* YND23_cmd, int32_t index_begin, int32_t index_end);
void print_ary_as_hex(uint8_t* ary, int32_t len);

int32_t test(int32_t argc, int8_t** argv){
	int32_t i = 0;

	uint8_t VER = 0;
	uint8_t ADR = 0;
	uint8_t CID1 = 0;
	uint8_t CID2 = 0;
	uint16_t LENGTH = 0;

	const char *psz_tip = NULL;
	uint8_t VER_real = 0x20;

	// LENGTH中的低12位 所能带的INFO数据个数为 2^12 = 4096;
	// INFO中的数据为WORD, WORD的最大数量为 4096/2 = 2048;
	uint8_t INFO[4096] = {'\0'}; // 如果LENGTH中的低12位是0, INFO域没内容, 不出现在产生的命令序列中

	// uint16_t CHKSUM = 0;

	// 最终拼好的命令序列最大长度 = SOI(1bytes) + VER(2bytes) + ADR(2bytes) + CID1(2bytes) + CID2(2bytes) + LENGTH(4bytes) + INFO(4096 max bytes) + CHKSUM(4bytes) + EOI(1byte) = 4906 + 18
	uint8_t YND23_cmd[4906 + 18] = {'\0'}; // 根据参数, 防止最终拼好的命令序列

	do {
		//setlocale(LC_ALL, ".936"); ///< 设置中文代码页, 用来显示中文字符
		// 不传参数进来了, 将参数写死，运行一次产生一个发送的命令序列
		// 如果要产生不同的命令, 重新写参数, 然后编译运行一次后, 产生一个新命令
		// 电总协议, 要给定以下6个参数(VER/ADR/CID1/CID2/LENGTH_for_INFO/INFO)
		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		//psz_tip = "这条指令只是验证计算和拼包, 用已知的正确包参数构造一个新包, 能还原原始包，说明拼包算法正确";
		//VER = 0x20; // 对于同一种设备, 通讯协议版本是固定的, 只要发一条命令取一下就行
		//ADR = 0x01; // 设备地址在设备仪表上有显示
		//CID1 = 0x42; // CID1/CID2的组合说明该命令具体任务
		//CID2 = 0x44;
		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));
		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
		printf("Exm电总后台通讯协议_V120.pdf\n");

	//1 获取系统模拟量量化数据（浮点数） 2AH 41H
		psz_tip = "1 获取系统模拟量量化数据（浮点数） 2AH 41H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x41;
		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
	//2 获取开关输入状态 2AH 43H
		psz_tip = "2 获取开关输入状态 2AH 43H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x43;
		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//3 获取告警状态 2AH 44H
		psz_tip = "3 获取告警状态 2AH 44H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x44;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//4 获取协议版本号 2AH 4FH
		psz_tip = "4 获取协议版本号 2AH 4FH";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x4F;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//5 获取设备地址 2AH 50H
		psz_tip = "5 获取设备地址 2AH 50H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x50;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//6 获取设备厂家信息 2AH 51H
		psz_tip = "6 获取设备厂家信息 2AH 51H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x51;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//7 获取自定义模拟量量化数据1（浮点数） 2AH E1H
		psz_tip = "7 获取自定义模拟量量化数据1（浮点数） 2AH E1H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE1;

		LENGTH = 1; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		INFO[0] = 0x00;

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//8 获取自定义模拟量量化数据2（浮点数） 2AH E2H
		psz_tip = "8 获取自定义模拟量量化数据2（浮点数） 2AH E2H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE2;

		LENGTH = 1; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		INFO[0] = 0x00;

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//9 获取自定义模拟量量化数据3（浮点数） 2AH E3H
		psz_tip = "9 获取自定义模拟量量化数据3（浮点数） 2AH E3H";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE3;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		//10 获取自定义模拟量量化数据4（浮点数） 2AH E7H
		psz_tip = "10 获取自定义模拟量量化数据4（浮点数） 2AH E7H 电池组1";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE7;

		LENGTH = 2; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		INFO[0] = 1;
		INFO[1] = 0;

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);


		//10 获取自定义模拟量量化数据4（浮点数） 2AH E7H
		psz_tip = "10 获取自定义模拟量量化数据4（浮点数） 2AH E7H 电池组2";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE7;

		LENGTH = 2; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));
		INFO[0] = 2;
		INFO[1] = 0;

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

//		 -----------------------------------------------------------------------------
//		 产生YDN23协议的发送命令
//		 -----------------------------------------------------------------------------
//		 获取通信协议版本号 2AH 4FH
		//psz_tip = "获取通信协议版本号";
		//VER = 0x00; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A; // CID1 = 0x2A, CID2 = 0x4F
		//CID2 = 0x4F;

		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));

		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

//		 回包
//		 7E 32 36 30 31 32 41 30 30 30 30 30 30 46 44 41 34 0D

//		 回包分析
//		 7E // SOI
//		 32 36 // VER = "26" = 2.6
//		 printf("%c%c\n", 0x32, 0x36);
//		 30 31 // ADR
//		 32 41 // CID1
//		 30 30 // RTN = 0x00(正常)
//		 30 30 30 30 // LENGTH = 0(NO INFO)
//		 46 44 41 34 // CHKSUM
//		 0D // EOI
		//VER_real = 0x26; // @todo 从回包中取到实际的VER
//		 -----------------------------------------------------------------------------
//		 产生YDN23协议的发送命令
//		 -----------------------------------------------------------------------------
//		 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
//		 获取设备厂家信息 2AH 51H
		//psz_tip = "获取设备厂家信息";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0x51;
		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));
		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
//		 产生的发包 ： 7E 32 36 30 31 32 41 35 31 30 30 30 30 46 44 39 45 0D
//		 -----------------------------------------------------------------------------
//		 产生YDN23协议的发送命令
//		 -----------------------------------------------------------------------------
//		 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
//		 获取告警状态 2AH 44H
		//psz_tip = "获取告警状态";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0x44;
		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));
		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
//		 -----------------------------------------------------------------------------
//		 产生YDN23协议的发送命令
//		 -----------------------------------------------------------------------------
//		 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
//		 获取开关输入状态 2AH 43H
		//psz_tip = "获取开关输入状态";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0x43;
		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));
		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
//		 -----------------------------------------------------------------------------
//		 产生YDN23协议的发送命令
//		 -----------------------------------------------------------------------------
//		 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
//		 获取自定义模拟量量化数据3 2AH E3H
		//psz_tip = "获取自定义模拟量量化数据3";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0xE3;
		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));
		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
//		 -----------------------------------------------------------------------------
//		 产生YDN23协议的发送命令
//		 -----------------------------------------------------------------------------
//		 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
//		 获取自定义模拟量量化数据2 2AH E2H
		//psz_tip = "获取自定义模拟量量化数据2";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0xE2;
		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));
		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
//		 -----------------------------------------------------------------------------
//		 产生YDN23协议的发送命令
//		 -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取自定义模拟量量化数据1 2AH E1H
		//psz_tip = "获取自定义模拟量量化数据1";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0xE1;
		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));
		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
//		 -----------------------------------------------------------------------------
//		 产生YDN23协议的发送命令
//		 -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取系统模拟量量化数据 2AH 41H
		//psz_tip = "获取系统模拟量量化数据";
		//VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		//ADR = 0x01; // 地址要正确
		//CID1 = 0x2A;
		//CID2 = 0x41;
		//LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		//memset(INFO, 0, sizeof(INFO));
		//calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
	}while(0);

	return EXIT_SUCCESS;
}

void calc_and_print_YND23_cmd(const char* psz_tip, uint8_t VER, uint8_t ADR, uint8_t CID1, uint8_t CID2, uint8_t* INFO, uint16_t LENGTH, uint8_t* YND23_cmd)
{
	// 一个有效的电总协议发送命令
	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // 命令序列最小为18个字节

	// 分析
	// 7E // SOI = 0x7E
	// 32 30 // VER = 0x20
	// 30 31 // ADR = 0x01
	// 34 32 // CID1 = 0x42
	// 34 34 // CID2 = 0x44
	// 30 30 30 30 // LENGTH = 0
	// INFO(no data)
	// 46 44 41 46 // CHKSUM = "FDAF" = 0xFDAF
	// 0D // EOI = 0x0D

	// CHKSUM = 0x0000; // 除了SOI/CHKSUM/EOI之外字节的累加和(4个字节一组进行累加)

	int32_t len_YND23_cmd = 0; // 最终拼好的命令有多少个字节?

	memset((char *)YND23_cmd, 0, sizeof(YND23_cmd)); // 除了SOI/EOI是16进制字节，其他的位置都要将16进制字节表示成2个ASCII字符标识的16进制数(e.g. 0x01 => "01" => 0x30 0x31)

	// 看看给定的参数，是否能还原上面的命令序列

	// 产生出的命令为YND23_cmd，字节长度为len_YND23_cmd
	len_YND23_cmd = gen_YND23_cmd(VER, ADR, CID1, CID2, LENGTH, INFO, YND23_cmd);

	// 打印出YND23_cmd的内容供贴到串口助手做测试
	printf("%s:\n", psz_tip);
	print_ary_as_hex(YND23_cmd, len_YND23_cmd);
	printf("请将回包贴在下面一行:\n");
	printf("\n\n");

	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // run result
	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // original cmd, CHKSUM = "FDAF" = 0xFDAF
	// ok :)
}

void print_ary_as_hex(uint8_t* ary, int32_t len)
{
	int32_t i = 0;

	for (i = 0; i < len; i++) {
		printf("%2.2X", ary[i]); // 为了贴到串口助手中直接用，不要换行
		if ((i + 1) != len) {
			printf(" ");
		}
		else {
			printf("\n");
		}
	}
}

int32_t gen_YND23_cmd(uint8_t VER, uint8_t ADR, uint8_t CID1, uint8_t CID2, uint16_t LENGTH, uint8_t* INFO, uint8_t* YND23_cmd)
{
	int32_t len = 0;
	int32_t ipos = 0;
	uint8_t c1 = '\0';
	uint8_t c2 = '\0';
	uint8_t c3 = '\0';
	uint8_t c4 = '\0';
	uint16_t LENGTH_have_ehck_sum = 0;
	uint16_t YDN23_CHKSUM = 0;
	int32_t i = 0;

	do {
		// @todo 不检查入参了

		// 7E // SOI = 0x7E
// 32 30 // VER = 0x20
// 30 31 // ADR = 0x01
// 34 32 // CID1 = 0x42
// 34 34 // CID2 = 0x44
// 30 30 30 30 // LENGTH = 0
// INFO(no data)
// 46 44 41 46 // CHKSUM = "FDAF" = 0xFDAF
// 0D // EOI = 0x0D

		// SOI
		YND23_cmd[ipos++] = SOI;

		// VER
		uint8_to_2ascii_int8_t(VER, &c1, &c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// ADR
		uint8_to_2ascii_int8_t(ADR, &c1, &c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// CID1
		uint8_to_2ascii_int8_t(CID1, &c1, &c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// CID2
		uint8_to_2ascii_int8_t(CID2, &c1, &c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// LENGTH
		LENGTH = LENGTH & (~(1 >> 12)); // 只取低12位, 最多4096(2^12)个数据
		LENGTH_have_ehck_sum = LENGTH_add_LCHKSUM(LENGTH);
		uint16_to_4ascii_int8_t(LENGTH_have_ehck_sum, &c1, &c2, &c3, &c4);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;
		YND23_cmd[ipos++] = c3;
		YND23_cmd[ipos++] = c4;

		// INFO
		for (i = 0; i < LENGTH; i++) {
			uint8_to_2ascii_int8_t(INFO[i], &c1, &c2);
			YND23_cmd[ipos++] = c1;
			YND23_cmd[ipos++] = c2;
		}
		//YDN23_CHKSUM = calc_YDN23_CHKSUM(YND23_cmd, 1 /*不要算SOI*/, ipos - 1 /*不算累加和本身和EOI*/);
		YDN23_CHKSUM = calc_YDN23_CHKSUM(YND23_cmd, 1 /*不要算SOI*/, ipos - 1 /*不算累加和本身和EOI*/);
		uint16_to_4ascii_int8_t(YDN23_CHKSUM, &c1, &c2, &c3, &c4);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;
		YND23_cmd[ipos++] = c3;
		YND23_cmd[ipos++] = c4;
		// EOI
		YND23_cmd[ipos++] = EOI;
		len = ipos;
	} while (0);
	return len;
}

int8_t fn_4bits_to_int8_t(uint8_t uc){
	int8_t c_out = '0';
	// 0 => '0'
	// 1 => '1'
	// 10 => 'A'
	// 11 => 'B'
	// 12 => 'C'
	// 13 => 'D'
	// 14 => 'E'
	// 15 => 'F'
	if ((uc >= 0x00) && (uc < 0x0A)){
		c_out = uc - 0 + '0';
	}else if ((uc >= 0x0A) && (uc <= 0x0F)){
		c_out = uc - 0x0A + 'A';
	}
	return c_out;
}

void uint8_to_2ascii_int8_t(uint8_t uc_in, uint8_t * c1, uint8_t * c2){
	// 0x12 => '1' '2'
	uint8_t uc1 = (uc_in >> 4) & 0x0f;
	uint8_t uc2 = (uc_in >> 0) & 0x0f;
	*c1 = fn_4bits_to_int8_t(uc1);
	*c2 = fn_4bits_to_int8_t(uc2);
}

void uint16_to_4ascii_int8_t(uint32_t ui_in, uint8_t * c1, uint8_t * c2, uint8_t * c3, uint8_t * c4){
	// 0x1234 => '1' '2' '3' '4'
	uint8_t uc1 = (uint8_t)((ui_in >> 12) & 0x0f);
	uint8_t uc2 = (uint8_t)((ui_in >> 8) & 0x0f);
	uint8_t uc3 = (uint8_t)((ui_in >> 4) & 0x0f);
	uint8_t uc4 = (uint8_t)((ui_in >> 0) & 0x0f);
	*c1 = fn_4bits_to_int8_t(uc1);
	*c2 = fn_4bits_to_int8_t(uc2);
	*c3 = fn_4bits_to_int8_t(uc3);
	*c4 = fn_4bits_to_int8_t(uc4);
}

uint16_t LENGTH_add_LCHKSUM(uint16_t LENGTH){
	if (LENGTH > 0) {
		LENGTH = LENGTH;
	}
	LENGTH *= 2; // 给定的长度是指的字节长度，在电总协议里面表示的是拆分后的字节长度，1个字变成了2个字节。
	uint16_t ui_out = 0;
	uint16_t ui_shift = 0;
	uint8_t uc1 = (LENGTH >> 8) & 0x0f;
	uint8_t uc2 = (LENGTH >> 4) & 0x0f;
	uint8_t uc3 = (LENGTH >> 0) & 0x0f;
	uint16_t uc_LCHKSUM = '\0'; // 高4位的电总校验和
	uc_LCHKSUM = uc1 + uc2 + uc3; // 低12位分为3个4位，进行累加
	uc_LCHKSUM %= 0x10; // 模16取余数
	uc_LCHKSUM = ~uc_LCHKSUM; // 取反
	uc_LCHKSUM += 1; // 加1
	ui_out = (uc_LCHKSUM << 12);
	ui_shift = (0x0F << 12);
	ui_out = ui_out & ui_shift; // 将算出的累加和放到高4位
	ui_out |= (LENGTH & 0xFFF); // 将LENGTH的低12位放到返回值的低12位
	return ui_out;
}

uint16_t calc_YDN23_CHKSUM(uint8_t* YND23_cmd, int32_t index_begin, int32_t index_end){
	// CHKSUM的计算是除SOI、 EOI和CHKSUM外，其他字符ASCII码值累加求和
	// 结果模65535取余数
	// 取反加1
	// YND23_cmd送进来时, 已经刨掉了SOI, CHKSUM, EOI
	int32_t i = 0;
	uint32_t ui_chksum = 0;
	uint16_t ui_chksum_rc = 0;
	for (i = index_begin; i <= index_end; i++) {
		ui_chksum += YND23_cmd[i];
	}
	ui_chksum %= 0xFFFF;
	ui_chksum = ~ui_chksum;
	ui_chksum += 1;
	ui_chksum_rc = ui_chksum & 0xffff;
	
	return ui_chksum_rc;
}
